#include "evaluator.h"
#include <deque>
#include <sstream>
#include <iomanip>
#include <iostream>
using namespace std;

Evaluator::Evaluator(FILE* fp)
{
  fg = new F_Grammar(fp); 
  topTree = fg->statementList();
  //fg->pp(topTree);
  topEnv = EG.newEnvironment();
  topEnv->sVal = "ROOT";
}


Evaluator::~Evaluator(void)
{
  myfile.close();
  EG.deleteEnvironment(topTree, 0);
  EG.deleteEnvironment(topEnv, 0);
}

void Evaluator::printParseTree()
{
  EG.print(topTree, 0);
}

void Evaluator::printEnvironment()
{
  EG.print(topEnv, 0);
}

void Evaluator::prettyPrint()
{
  fg->pp(topTree);
}

Lexeme* Evaluator::start()
{
  Lexeme* l = eval(topTree, topEnv);
  //EG.print(topEnv, 0);
  return l;
}

Lexeme* Evaluator::eval(Lexeme* tree, Lexeme* env)
{
  if (strcmp(tree->tVal, SSQ) == 0)
    return evalSSQ(tree, env);
  if (strcmp(tree->tVal, COND_WHILE) == 0)
    return evalWhile(tree, env);
  if (strcmp(tree->tVal, FUNCDEF) == 0)
    return EG.extend(tree, evalFuncDef(tree, env), env);
  if (strcmp(tree->tVal, OP_ASN) == 0)
    return evalAssign(tree, env);
  if (strcmp(tree->tVal, FUNC_CALL) == 0)
    return evalFuncCall(tree, env);
  if (strcmp(tree->tVal, EXPR) == 0)
    return evalExpression(tree, env);
  if (strcmp(tree->tVal, INTEGER) == 0)
    return tree;
  if (strcmp(tree->tVal, ID) == 0)
    return EG.lookup(tree, env);
  if (strcmp(tree->tVal, VARDEF) == 0)
    return EG.extend(tree, evalVarDef(tree, env), env);
  if (strcmp(tree->tVal, DISPLAY) == 0)
    return evalDisplay(tree, env);
  if (strcmp(tree->tVal, DISPLAYN) == 0)
    return evalDisplay(tree, env);
  else
    return new Lexeme(BAD_CHAR,-1);
}

Lexeme* Evaluator::evalDisplay(Lexeme* tree, Lexeme* env)
{
  Lexeme* val = eval(EG.cadr(tree),env);
  if (strcmp(val->tVal,INTEGER) == 0)
    printf("%d",val->iVal);
  if(strcmp(val->tVal, STRING) == 0)
    printf("%s", val->sVal.c_str());
  if(strcmp(tree->tVal,DISPLAYN) == 0)
    printf("\n");
  return val;
}
Lexeme* Evaluator::evalSSQ(Lexeme* tree, Lexeme* env)
{
  Lexeme* last = NULL;
  while (tree != NULL)
  {
    last = eval(EG.car(tree), env);
    tree = EG.cdr(tree);
  }
  return last;
}

Lexeme* Evaluator::evalWhile(Lexeme* tree, Lexeme* env)
{
  Lexeme* last = NULL;
  while (isTrue(eval(EG.cadr(tree),env)))
  {
    last = eval(EG.car(EG.caddr(tree)), env);
  }
  return last;
}

bool Evaluator::isTrue(Lexeme* a)
{
  return a->iVal == 0 ? false : true;
}

Lexeme* Evaluator::intOp(Lexeme* a, Lexeme* b, const char* op)
{
  int cv = a->iVal;
  int dv = b->iVal;

  if (strcmp(op, AOP_ADD) == 0)
    return new Lexeme(a->tVal,cv+dv,a->getLine());
  else if (strcmp(op, AOP_SUB) == 0)
    return new Lexeme(a->tVal,cv-dv,a->getLine());
  else if (strcmp(op, AOP_MUL) == 0)
    return new Lexeme(a->tVal,cv*dv,a->getLine());
  else if (strcmp(op, AOP_DIV) == 0)
    return new Lexeme(a->tVal,cv/dv,a->getLine());
  else if (strcmp(op, AOP_MOD) == 0)
    return new Lexeme(a->tVal,cv%dv,a->getLine());
  else if (strcmp(op, LOP_AND) == 0)
      return new Lexeme(a->tVal,cv && dv,a->getLine());
  else if (strcmp(op, LOP_OR) == 0)
      return new Lexeme(a->tVal,cv || dv,a->getLine());
  else if (strcmp(op, BOP_AND) == 0)
      return new Lexeme(a->tVal,cv & dv,a->getLine());
  else if (strcmp(op, BOP_OR) == 0)
      return new Lexeme(a->tVal,cv | dv,a->getLine());
  else if (strcmp(op, BOP_XOR) == 0)
      return new Lexeme(a->tVal,cv ^ dv,a->getLine());
  else if (strcmp(op, BOP_LSFT) == 0)
      return new Lexeme(a->tVal,cv << dv,a->getLine());
  else if (strcmp(op, BOP_RSFT) == 0)
      return new Lexeme(a->tVal,cv >> dv,a->getLine());
  else if (strcmp(op, COP_EQUAL) == 0)
      return new Lexeme(a->tVal,cv == dv ? 1 : 0,a->getLine());
  else if (strcmp(op, COP_NEQUAL) == 0)
      return new Lexeme(a->tVal,cv != dv ? 1 : 0,a->getLine());
  else if (strcmp(op, COP_GREATER) == 0)
      return new Lexeme(a->tVal,cv > dv ? 1 : 0,a->getLine());
  else if (strcmp(op, COP_LESS) == 0)
  {
    int r = cv < dv ? 1 : 0;
    return new Lexeme(a->tVal,r,a->getLine());
  }
  else if (strcmp(op, COP_GRTEQU) == 0)
      return new Lexeme(a->tVal,cv >= dv ? 1 : 0,a->getLine());
  else if (strcmp(op, COP_LESEQU) == 0)
      return new Lexeme(a->tVal,cv <= dv ? 1 : 0,a->getLine());
  else
    return new Lexeme(BAD_CHAR,-1);
}

Lexeme* Evaluator::stringOp(Lexeme* a, Lexeme* b, const char* op)
{
  std::string c = a->getSVal();
    std::string d = b->getSVal();
  int cs = c.length();
  int ds = d.length();

  if (strcmp(op, AOP_ADD) == 0)
  {
    std::string s;
    s.append(c);
    s.append(d);
    return new Lexeme(STRING, s, a->getLine());
  }
  else if(strcmp(op, COP_EQUAL) == 0)
  {
    if(strcmp(c.c_str(), d.c_str()) == 0)
      return new Lexeme(INTEGER,1,a->getLine());
    else
      return new Lexeme(INTEGER,0,a->getLine());
  }
  else if (strcmp(op, COP_NEQUAL) == 0)
  {
    if(strcmp(c.c_str(), d.c_str()) == 0)
      return new Lexeme(INTEGER,0,a->getLine());
    else
      return new Lexeme(INTEGER,1,a->getLine());
  }
  else if (strcmp(op, COP_GREATER) == 0)
  {
      return new Lexeme(INTEGER,cs > ds,a->getLine());
  }
  else if (strcmp(op, COP_LESS) == 0)
  {
      return new Lexeme(INTEGER,cs < ds,a->getLine());
  }
  else if (strcmp(op, COP_GRTEQU) == 0)
  {
      return new Lexeme(INTEGER,cs >= ds,a->getLine());
  }
  else if (strcmp(op, COP_LESEQU) == 0)
  {
      return new Lexeme(INTEGER,cs <= ds,a->getLine());
  }
  else
    return new Lexeme(BAD_CHAR,-1);
}

Lexeme* Evaluator::evalOp(Lexeme* tree, Lexeme* op, Lexeme* env)
{
  Lexeme* a = eval(EG.car(tree), env);
  Lexeme* b = eval(EG.caddr(tree), env);
  if(a->tVal == NULL)
    a = eval(EG.car(a), env);
  if (strcmp(a->tVal, INTEGER) == 0)
    return intOp(a, b, op->tVal);
  else
    return stringOp(a, b, op->tVal);
}

Lexeme* Evaluator::evalVarDef(Lexeme* tree, Lexeme* env)
{
  Lexeme* init = eval(EG.cdr(tree), env);
  return EG.populate(EG.car(tree), init, env);
}

Lexeme* Evaluator::evalID(Lexeme* tree, Lexeme* env)
{
  return EG.lookup(tree, env);
}

Lexeme* Evaluator::evalAssign(Lexeme* tree, Lexeme* env)
{
  return EG.update(EG.car(tree), eval(EG.caddr(tree), env), env);
}

Lexeme* Evaluator::evalFuncDef(Lexeme* tree, Lexeme* env)
{
  return EG.populate(EG.car(tree), EG.cons(env, tree), env);
}

Lexeme* Evaluator::evalFuncCall(Lexeme* tree, Lexeme* env)
{  
  Lexeme* args = evalList(EG.cadr(tree), env);  

  Lexeme* func = EG.lookup(EG.car(tree), env);

  Lexeme* params = getParams(func);
  Lexeme* defEnv = getDefEnv(func);

  Lexeme* evalEnv = EG.extend(params, args, defEnv);
  
  Lexeme* q = EG.lookup(EG.car(params),evalEnv);
  Lexeme* r = EG.lookup(EG.cadr(params), evalEnv);
  Lexeme* s = EG.lookup(EG.caddr(params), evalEnv);
  
  Lexeme* body = getBody(func);
  Lexeme* rtr = eval(body, evalEnv);
  return rtr;
}

//TODO: FINISH!!!!
Lexeme* Evaluator::evalList(Lexeme* tree, Lexeme* env) 
{
  Lexeme* r = NULL;
  if(tree != NULL)
  {
    Lexeme* s = eval(EG.car(tree),env);
    return EG.cons(s, evalList(EG.cdr(tree), env));
  }
  else
    return NULL;
}
//TODO: finish getParams
Lexeme* Evaluator::getParams(Lexeme* func)
{
  return EG.caddr(func);
}

//TODO: finish getDefEnv
Lexeme* Evaluator::getDefEnv(Lexeme* func)
{
  return EG.car(func);
}

//TODO: finish getBody
Lexeme* Evaluator::getBody(Lexeme* func)
{
  return EG.car(EG.cadddr(func));
}

Lexeme* Evaluator::evalExpression(Lexeme* tree, Lexeme* env)
{
  Lexeme* id = EG.lookup(EG.car(tree), env);
  Lexeme* op = EG.cadr(tree);
  Lexeme* expr = EG.caddr(tree);

  if(strcmp(op->tVal,OP_ASN) == 0)
    return evalAssign(tree,env);
  else
    return evalOp(tree,op, env);
}
